/** * *************************************************************************** * Copyright (c) 2010 Qcadoo Limited * Project: Qcadoo Framework * Version: 1.4 * * This file is part of Qcadoo. * * Qcadoo is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************** */ package com.qcadoo.model.api.search; import java.util.Collection; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.hibernate.criterion.Conjunction; import org.hibernate.criterion.Disjunction; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Restrictions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.qcadoo.model.api.DataDefinition; import com.qcadoo.model.api.Entity; import com.qcadoo.model.internal.api.DataAccessService; import com.qcadoo.model.internal.api.InternalDataDefinition; import com.qcadoo.model.internal.search.InExpressionIgnoringCase; import com.qcadoo.model.internal.search.SearchConjunctionImpl; import com.qcadoo.model.internal.search.SearchCriterionImpl; import com.qcadoo.model.internal.search.SearchDisjunctionImpl; /** * Utility with factory methods for {@link SearchCriterion}. * * @since 0.4.1 */ @Component public final class SearchRestrictions { private static DataAccessService dataAccessService; private static final String[] QCADOO_WILDCARDS = new String[] { "*", "?" }; private static final String[] HIBERNATE_WILDCARDS = new String[] { "%", "_" }; private static void setStaticDataAccessService(final DataAccessService dataAccessService) { SearchRestrictions.dataAccessService = dataAccessService; } @Autowired protected void setDataAccessService(final DataAccessService dataAccessService) { SearchRestrictions.setStaticDataAccessService(dataAccessService); } /** * Match mode for "like" criterion. */ public static enum SearchMatchMode { /** * Match anywhere. */ ANYWHERE(MatchMode.ANYWHERE), /** * Match at the end. */ END(MatchMode.END), /** * Match exact value. */ EXACT(MatchMode.EXACT), /** * Match at the beginning. */ START(MatchMode.START); private final MatchMode matchMode; private SearchMatchMode(final MatchMode matchMode) { this.matchMode = matchMode; } public MatchMode getHibernateMatchMode() { return matchMode; } } /** * Creates restriction which join given restrictions with "OR" operator. * * @param firstCriterion * first criterion * @param secondCriterion * second criterion * @param otherCriteria * other criteria * @return criterion */ public static SearchCriterion or(final SearchCriterion firstCriterion, final SearchCriterion secondCriterion, final SearchCriterion... otherCriteria) { Disjunction disjunction = Restrictions.disjunction(); disjunction.add(firstCriterion.getHibernateCriterion()); disjunction.add(secondCriterion.getHibernateCriterion()); for (SearchCriterion criterion : otherCriteria) { disjunction.add(criterion.getHibernateCriterion()); } return new SearchCriterionImpl(disjunction); } /** * Creates restriction which join given restrictions with "AND" operator. * * @param firstCriterion * first criterion * @param secondCriterion * second criterion * @param otherCriteria * other criteria * @return criterion */ public static SearchCriterion and(final SearchCriterion firstCriterion, final SearchCriterion secondCriterion, final SearchCriterion... otherCriteria) { Conjunction conjunction = Restrictions.conjunction(); conjunction.add(firstCriterion.getHibernateCriterion()); conjunction.add(secondCriterion.getHibernateCriterion()); for (SearchCriterion criterion : otherCriteria) { conjunction.add(criterion.getHibernateCriterion()); } return new SearchCriterionImpl(conjunction); } /** * Wraps given criterion with "not" criterion. * * @param criterion * criterion * @return negated criterion */ public static SearchCriterion not(final SearchCriterion criterion) { return new SearchCriterionImpl(Restrictions.not(criterion.getHibernateCriterion())); } /** * Creates criterion which checks if id is equal to given value. * * @param value * value * @return criterion */ public static SearchCriterion idEq(final long value) { return new SearchCriterionImpl(Restrictions.idEq(value)); } /** * Creates criterion which checks if id isn't equal to given value. * * @param value * value * @return criterion */ public static SearchCriterion idNe(final long value) { return new SearchCriterionImpl(Restrictions.not(Restrictions.idEq(value))); } /** * Creates criterion which checks if all given fields match given values. * * @param values * map where key is a field's name and value is the expected value * @return criterion */ public static SearchCriterion allEq(final Map<String, Object> values) { return new SearchCriterionImpl(Restrictions.allEq(values)); } /** * Creates criterion which checks if field is equal (using "like" operator) to given value. * * @param field * field * @param value * value * @return criterion */ public static SearchCriterion like(final String field, final String value) { if (value == null) { return isNull(field); } return new SearchCriterionImpl(Restrictions.like(field, convertWildcards(value))); } /** * Creates criterion which checks if field is equal (using "like" operator) to given value. * * @param field * field * @param mode * match mode * @param value * value * @return criterion */ public static SearchCriterion like(final String field, final String value, final SearchMatchMode mode) { if (value == null) { return isNull(field); } return new SearchCriterionImpl(Restrictions.like(field, convertWildcards(value), mode.getHibernateMatchMode())); } /** * Creates criterion which checks if field is equal (using case-insensitive "like" operator) to given value. * * @param field * field * @param value * value * @return criterion */ public static SearchCriterion ilike(final String field, final String value) { if (value == null) { return isNull(field); } return new SearchCriterionImpl(Restrictions.ilike(field, convertWildcards(value))); } /** * Creates criterion which checks if field is equal (using case-insensitive "like" operator) to given value. * * @param field * field * @param mode * match mode * @param value * value * @return criterion */ public static SearchCriterion ilike(final String field, final String value, final SearchMatchMode mode) { if (value == null) { return isNull(field); } return new SearchCriterionImpl(Restrictions.ilike(field, convertWildcards(value), mode.getHibernateMatchMode())); } private static String convertWildcards(final String value) { return StringUtils.replaceEach(value, QCADOO_WILDCARDS, HIBERNATE_WILDCARDS); } /** * Creates criterion which checks if field is less than or equal to given value. * * @param field * field * @param value * value * @return criterion */ public static SearchCriterion le(final String field, final Object value) { return new SearchCriterionImpl(Restrictions.le(field, value)); } /** * Creates criterion which checks if field is less than given value. * * @param field * field * @param value * value * @return criterion */ public static SearchCriterion lt(final String field, final Object value) { return new SearchCriterionImpl(Restrictions.lt(field, value)); } /** * Creates criterion which checks if field is greater than or equal to given value. * * @param field * field * @param value * value * @return criterion */ public static SearchCriterion ge(final String field, final Object value) { return new SearchCriterionImpl(Restrictions.ge(field, value)); } /** * Creates criterion which checks if field is greater than given value. * * @param field * field * @param value * value * @return criterion */ public static SearchCriterion gt(final String field, final Object value) { return new SearchCriterionImpl(Restrictions.gt(field, value)); } /** * Creates criterion which checks if field isn't equal to given value. * * @param field * field * @param value * value * @return criterion */ public static SearchCriterion ne(final String field, final Object value) { return new SearchCriterionImpl(Restrictions.ne(field, value)); } /** * Creates criterion which checks if field is equal to given value. * * @param field * field * @param value * value * @return criterion */ public static SearchCriterion eq(final String field, final Object value) { return new SearchCriterionImpl(Restrictions.eq(field, value)); } /** * Creates criterion which checks if field is equal (using case-insensitive "eq" operator) to given value. * * @param field * field * @param value * value * @return criterion */ public static SearchCriterion iEq(final String field, final Object value) { return new SearchCriterionImpl(Restrictions.eq(field, value).ignoreCase()); } /** * Creates criterion which checks if field is less than or equal to other field. * * @param field * field * @param otherField * other field * @return criterion */ public static SearchCriterion leField(final String field, final String otherField) { return new SearchCriterionImpl(Restrictions.leProperty(field, otherField)); } /** * Creates criterion which checks if field is less than other field. * * @param field * field * @param otherField * other field * @return criterion */ public static SearchCriterion ltField(final String field, final String otherField) { return new SearchCriterionImpl(Restrictions.ltProperty(field, otherField)); } /** * Creates criterion which checks if field is greater than or equal to other field. * * @param field * field * @param otherField * other field * @return criterion */ public static SearchCriterion geField(final String field, final String otherField) { return new SearchCriterionImpl(Restrictions.geProperty(field, otherField)); } /** * Creates criterion which checks if field is greater than other field. * * @param field * field * @param otherField * other field * @return criterion */ public static SearchCriterion gtField(final String field, final String otherField) { return new SearchCriterionImpl(Restrictions.gtProperty(field, otherField)); } /** * Creates criterion which checks if field isn't equal to other field. * * @param field * field * @param otherField * other field * @return criterion */ public static SearchCriterion neField(final String field, final String otherField) { return new SearchCriterionImpl(Restrictions.neProperty(field, otherField)); } /** * Creates criterion which checks if field is equal to other field. * * @param field * field * @param otherField * other field * @return criterion */ public static SearchCriterion eqField(final String field, final String otherField) { return new SearchCriterionImpl(Restrictions.eqProperty(field, otherField)); } /** * Creates criterion which checks if field is not null. * * @param field * field * @return criterion */ public static SearchCriterion isNotNull(final String field) { return new SearchCriterionImpl(Restrictions.isNotNull(field)); } /** * Creates criterion which checks if field is null. * * @param field * field * @return criterion */ public static SearchCriterion isNull(final String field) { return new SearchCriterionImpl(Restrictions.isNull(field)); } /** * Creates criterion which checks if "collection" field's size is equal to given size. * * @param field * field * @param size * size * @return criterion */ public static SearchCriterion sizeEq(final String field, final int size) { return new SearchCriterionImpl(Restrictions.sizeEq(field, size)); } /** * Creates criterion which checks if "collection" field's size is less than or equal to given size. * * @param field * field * @param size * size * @return criterion */ public static SearchCriterion sizeLe(final String field, final int size) { return new SearchCriterionImpl(Restrictions.sizeLe(field, size)); } /** * Creates criterion which checks if "collection" field's size is less than given size. * * @param field * field * @param size * size * @return criterion */ public static SearchCriterion sizeLt(final String field, final int size) { return new SearchCriterionImpl(Restrictions.sizeLt(field, size)); } /** * Creates criterion which checks if "collection" field's size is greater than or equal to given size. * * @param field * field * @param size * size * @return criterion */ public static SearchCriterion sizeGe(final String field, final int size) { return new SearchCriterionImpl(Restrictions.sizeGe(field, size)); } /** * Creates criterion which checks if "collection" field's size is greater than given size. * * @param field * field * @param size * size * @return criterion */ public static SearchCriterion sizeGt(final String field, final int size) { return new SearchCriterionImpl(Restrictions.sizeGt(field, size)); } /** * Creates criterion which checks if "collection" field's size isn't equal to given size. * * @param field * field * @param size * size * @return criterion */ public static SearchCriterion sizeNe(final String field, final int size) { return new SearchCriterionImpl(Restrictions.sizeNe(field, size)); } /** * Creates criterion which checks if "collection" field's size is empty. * * @param field * field * @return criterion */ public static SearchCriterion isEmpty(final String field) { return new SearchCriterionImpl(Restrictions.isEmpty(field)); } /** * Creates criterion which checks if "collection" field's size isn't empty. * * @param field * field * @return criterion */ public static SearchCriterion isNotEmpty(final String field) { return new SearchCriterionImpl(Restrictions.isNotEmpty(field)); } /** * Creates criterion which checks if field is between given values. * * @param field * field * @param lo * low value * @param hi * high value * @return criterion */ public static SearchCriterion between(final String field, final Object lo, final Object hi) { return new SearchCriterionImpl(Restrictions.between(field, lo, hi)); } /** * Creates criterion which checks if field is in given values. * * @param field * field * @param values * values * @return criterion */ public static SearchCriterion in(final String field, final Collection<?> values) { return new SearchCriterionImpl(Restrictions.in(field, values)); } /** * Creates criterion which checks if field is in given values ignoring case. * * @param field * field * @param values * values * @return criterion */ public static SearchCriterion inIgnoringCase(final String field, final Collection<?> values) { return new SearchCriterionImpl(new InExpressionIgnoringCase(field, values.toArray())); } /** * Creates criterion which checks if field is in given values. * * @param field * field * @param values * values * @return criterion */ public static SearchCriterion in(final String field, final Object... values) { return new SearchCriterionImpl(Restrictions.in(field, values)); } /** * Creates criterion which checks if "belongsTo" field is equal to given entity. * * Current implementation of this method performs Entity loading and may cause StackOverflowError when used (for example) * within onView hook. * * Therefore prefer belongsTo(String, Entity) and use this method only in case when you can't obtain reference to a whole * Entity. * * @param field * field * @param pluginIdentifier * plugin's identifier * @param modelName * model's name * @param id * id * @return criterion */ public static SearchCriterion belongsTo(final String field, final String pluginIdentifier, final String modelName, final long id) { return belongsTo(field, dataAccessService.getDataDefinition(pluginIdentifier, modelName), id); } /** * Creates criterion which checks if "belongsTo" field is equal to given entity. * * Current implementation of this method performs Entity loading and may cause StackOverflowError when used (for example) * within onView hook. * * Therefore prefer belongsTo(String, Entity) and use this method only in case when you can't obtain reference to a whole * Entity. * * @param field * field * @param dataDefinition * data's definition * @param id * id * @return criterion */ public static SearchCriterion belongsTo(final String field, final DataDefinition dataDefinition, final long id) { return belongsTo(field, dataAccessService.get((InternalDataDefinition) dataDefinition, id)); } /** * Creates criterion which checks if "belongsTo" field is equal to given entity. * * @param field * field * @param entity * entity * @return criterion */ public static SearchCriterion belongsTo(final String field, final Entity entity) { Object databaseEntity = null; if (entity != null) { databaseEntity = dataAccessService.convertToDatabaseEntity(entity); } if (databaseEntity == null) { return isNull(field); } else { return eq(field, databaseEntity); } } /** * Creates disjunction - (... OR ... OR ...). * * @return disjunction */ public static SearchDisjunction disjunction() { return new SearchDisjunctionImpl(); } /** * Creates conjunction - (... AND ... AND ...). * * @return conjunction */ public static SearchConjunction conjunction() { return new SearchConjunctionImpl(); } }